/**
 * 
 */
package com.zznode.client.wlanpolling.test;

import java.io.ByteArrayInputStream;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.SortedMap;

import javax.xml.xpath.XPathConstants;

import org.achartengine.GraphicalView;
import org.achartengine.chart.DialChart;
import org.achartengine.chart.PointStyle;
import org.achartengine.chart.TimeChart;
import org.achartengine.model.CategorySeries;
import org.achartengine.model.TimeSeries;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.renderer.DialRenderer;
import org.achartengine.renderer.DialRenderer.Type;
import org.achartengine.renderer.SimpleSeriesRenderer;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.w3c.dom.Node;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.zznode.client.wlanpolling.R;
import com.zznode.client.wlanpolling.WLANPollingApplication;
import com.zznode.client.wlanpolling.service.Wifi;
import com.zznode.client.wlanpolling.service.net.EnumHttpMethod;
import com.zznode.client.wlanpolling.service.net.HttpCallback;
import com.zznode.client.wlanpolling.service.net.HttpException;
import com.zznode.client.wlanpolling.service.net.HttpExecutorFactory;
import com.zznode.client.wlanpolling.service.net.HttpGetExecutor;
import com.zznode.client.wlanpolling.service.net.HttpParam;
import com.zznode.client.wlanpolling.service.xml.XmlParserException;
import com.zznode.client.wlanpolling.service.xml.XmlXPathAdaptor;
import com.zznode.client.wlanpolling.utils.WLanPollingUtils;

/**
 * @author Jerome
 * 
 */
public abstract class FTPRateChartActivity extends Activity implements HttpCallback {
	
	String host = "";
	String port = "";
	String username = "";
	String password = "";
	String dir = "";
	String fileName = "";
	
	// Constants
	private double MIN = 0;
	private double MAX = 1024 * 10;// 10M
	
	double R256 = 256;
	double R512 = 512;
	double R1024 = 1024;
	double R3072 = 3072;
	double R5120 = 5120;
	double R10240 = 10240;
	
	protected long allSize = 0;
	protected long completedSize = 0;
	protected double currentSpeed = 0;
	protected double averageSpeed = 0;
	protected double maxSpeed = 0;
	
	protected GraphicalView dashboardChart;
	protected CategorySeries category = new CategorySeries("");
	protected DialRenderer dashboardRenderer = new DialRenderer();
	
	protected GraphicalView chartView;
	protected XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
	protected XYMultipleSeriesRenderer chartRenderer = new XYMultipleSeriesRenderer();
	
	protected Bitmap dashboard = null;
	protected Bitmap center = null;
	protected Bitmap pointer = null;
	protected ProgressDialog pd = null;
	
	protected Button btn_starttest = null;
	protected Button btn_stoptest = null;
	
	protected TextView tv_testCount = null;
	protected TextView tv_curSpeed = null;
	protected TextView tv_avgSpeed = null;
	
	protected TextView tv_completed = null;
	protected TextView tv_allsize = null;
	protected TextView tv_percent = null;
	
	protected TextView tv_testresult = null;
	
	protected int testCount = 0;
	protected int testNumber = 5;
	
	protected boolean stopped = true;
	
	private String bssid = null;
	protected WLANPollingApplication application;
	
	// Use a thread to refresh the chart periodically
	protected Thread refreshThread = null;
	private boolean useThreadToRefreshCharts = false; // change this to true if still has issue in refreshing charts
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		application = (WLANPollingApplication)getApplication();
		
		setContentView(R.layout.polltest_ftp);
		
		String id = null;
		if(application != null){
			testNumber = application.getContext().getSystemSetting().getFtpTestCount();
			Wifi wifi = application.getContext().getCurrentWifi();
			if(wifi != null){
				id = wifi.getBSSID();
			}
		}
		if(bssid == null || !bssid.equals(id)){
			bssid = id;
			getFTPSettings();
		}
		populateUI();
		dashboard = BitmapFactory.decodeResource(getResources(),R.drawable.pic_dashboard);
		center = BitmapFactory.decodeResource(getResources(),R.drawable.center);
		pointer = BitmapFactory.decodeResource(getResources(),R.drawable.pointer);
		if (useThreadToRefreshCharts) {
			refreshThread = new Thread() {
				public void run() {
					while (true) {
						try {
							Thread.sleep(300);
							updateChart();
						} catch (InterruptedException e) {
						}
					}
				}
			};
		}
	}
	
	@Override
	public void onBackPressed() {
		if(!stopped){
			stopTest();
		}
		super.onBackPressed();
	}

	private void getFTPSettings(){
		pd = new ProgressDialog(this);
		pd.setTitle("FTP测试");
		pd.setMessage("正在获取FTP信息，请稍候...");
		pd.show();
		
		HttpGetExecutor executor = (HttpGetExecutor)HttpExecutorFactory.createHttpExecutor(EnumHttpMethod.GET);
		executor.setUrl(WLanPollingUtils.getListPhoneFtpUrl());
		
		executor.addParam(new HttpParam("UID", application.getContext().getUid()));
		
		executor.asyncExecute(this, 0);
	}
	
	private void populateUI(){
		String label = getLabel();
		TextView tv = (TextView)this.findViewById(R.id.ftptest_label);
		tv.setText(label);
		btn_starttest = (Button)this.findViewById(R.id.ftptest_btn);
		btn_starttest.setText("测试");
		btn_starttest.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				startTest();
			}
			
		});
		tv_testCount = (TextView)this.findViewById(R.id.ftptest_testcount);
		tv_testCount.setText("0/" + testNumber);
		tv_curSpeed = (TextView)this.findViewById(R.id.ftptest_crtrate);
		tv_avgSpeed = (TextView)this.findViewById(R.id.ftptest_avgrate);
		createChartPart();
		tv_completed = (TextView)this.findViewById(R.id.ftptest_summary_complete);
		tv_allsize = (TextView)this.findViewById(R.id.ftptest_summary_allsize);
		tv_percent = (TextView)this.findViewById(R.id.ftptest_summary_percent);
		createTestResultPart();
		Button btn = (Button)this.findViewById(R.id.ftptest_savetest);
		btn.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				doSave();
			}
			
		});
	}
	
	protected abstract String getLabel();
	protected abstract void doFTPOperation(FTPClient client) throws Exception;
	protected abstract boolean isValidForTesting();
	protected abstract void doSave();
	
	protected void startTest(){
		if(!isValidForTesting()){
			return;
		}
		testCount = 0;
		tv_testresult.setText("");
		btn_starttest.setEnabled(false);
		btn_stoptest.setEnabled(true);
		dataset.getSeriesAt(0).clear();
		dataset.getSeriesAt(0).add(0, 0.1);
		dashboardChart.invalidate();
		chartView.invalidate();
		startFTPThread();
	}
	
	protected void stopTest() {
		stopped = true;
		btn_starttest.setEnabled(true);
		btn_stoptest.setEnabled(false);
	}
	
	protected void appendTestResult(int newCount){
		StringBuffer buffer = new StringBuffer();
		buffer.append("\n测试");
		buffer.append(newCount);
		buffer.append(" - 完成数据：");
		buffer.append(completedSize / 1024);
		buffer.append("KB 共有数据：");
		buffer.append(allSize / 1024);
		buffer.append("KB 完成：");
		if(allSize > 0 && completedSize > 0){
			double ratio = (double)completedSize / (double)allSize * 100;
			NumberFormat format = NumberFormat.getInstance();
			format.setMaximumFractionDigits(2);
			buffer.append(format.format(ratio));
			buffer.append("%");
		}
		buffer.append(" 平均速率：");
		buffer.append(tv_avgSpeed.getText());
		tv_testresult.append(buffer.toString());
	}
	
	protected void updateSpeed(){
		this.runOnUiThread(new Runnable(){
			public void run(){
				tv_curSpeed.setText(currentSpeed + "KB/s");
				tv_avgSpeed.setText(averageSpeed + "KB/s");
			}
		});
	}
	
	protected void updateSummary(){
		this.runOnUiThread(new Runnable(){
			public void run(){
				tv_completed.setText(completedSize / 1024 + "KB");
				tv_allsize.setText(allSize / 1024 + "KB");
				if(allSize > 0 && completedSize > 0){
					double ratio = (double)completedSize / (double)allSize * 100;
					NumberFormat format = NumberFormat.getInstance();
					format.setMaximumFractionDigits(2);
					tv_percent.setText(format.format(ratio) + "%");
				}
			}
		});
	}
	
	protected void updateChart(){
		if(currentSpeed > maxSpeed){
			maxSpeed = currentSpeed;
			chartRenderer.setYAxisMax(maxSpeed == 0 ? 50 : maxSpeed * 1.3);
		}		
		dashboardChart.postInvalidate();
		chartView.postInvalidate();
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		if(refreshThread != null){
			if(!refreshThread.isAlive()){
				refreshThread.start();
			} else {
				refreshThread.resume();
			}
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
		if(refreshThread != null){
			refreshThread.suspend();
		}
	}

	protected void uiRun(Runnable runnable){
		this.runOnUiThread(runnable);
	}
	
	private void createChartPart(){
		initCharts();
		
//		LayoutParams params = new LayoutParams(480, 200);
////		LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
//		params.gravity = Gravity.CENTER_HORIZONTAL;
//		layout.addView(dashboardChart, params);
//		
//		params = new LayoutParams(480, 200);
////		params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
//		params.gravity = Gravity.CENTER_HORIZONTAL;
//		layout.addView(chartView, params);
	}
	
	private void createTestResultPart(){
		btn_stoptest = (Button)this.findViewById(R.id.ftptest_stoptest);
		btn_stoptest.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				stopTest();
			}
			
		});
		btn_stoptest.setEnabled(false);
		tv_testresult = (TextView)this.findViewById(R.id.ftptest_testresult);
	}
	
	private void viewResult(){
//		TextView tv = (TextView)this.findViewById(R.id.ftptest_summary);
//		String result = getTestResult();
//		tv.setText(result);
	}
	
	protected void initCharts(){
		initDashboardRenderer();
		initChartRenderer();
		
		DialChart _chart = new DialChart(category, dashboardRenderer){

			@Override
			public void draw(Canvas canvas, int x, int y, int width,
					int height, Paint paint) {
				canvas.drawBitmap(dashboard, x + 65, y, paint);
				super.draw(canvas, x, y, width, height, paint);
			}

			@Override
			protected void drawTicks(Canvas canvas, double min, double max,
					double minAngle, double maxAngle, int centerX, int centerY,
					double longRadius, double shortRadius, double ticks,
					Paint paint, boolean labels) {
//				super.drawTicks(canvas, min, max, minAngle, maxAngle, centerX, centerY,
//						longRadius, shortRadius, ticks, paint, labels);
			}

			@Override
			protected void drawNeedle(Canvas canvas, double angle, int centerX,
					int centerY, double radius, boolean arrow, Paint paint) {
				if(Double.isNaN(angle)){
					angle = 4.974188368183839;
				}
				centerY += 10;
				canvas.save();
				canvas.translate(0, centerY);
				double realAngle = 360 * angle / Math.toRadians(360);
//				double diff = this.mRenderer.getAngleMin() - realAngle;
				canvas.rotate((float)(90 - 180 - realAngle), centerX, 0);
				canvas.drawBitmap(pointer, pointer.getWidth(), 0 - pointer.getHeight() / 2, paint);
				canvas.restore();
				canvas.drawBitmap(center, centerX - center.getWidth() / 2, centerY - center.getHeight() / 2, paint);
//				super.drawNeedle(canvas, angle, centerX, centerY, radius, arrow, paint);
			}
			
		};
//	    dashboardChart = new GraphicalView(this, _chart){
//
//			@Override
//			public boolean onTouchEvent(MotionEvent event) {
//				return false;
//			}
//	    	
//	    };
		dashboardChart = (GraphicalView)this.findViewById(R.id.ftptest_dashboard);
		dashboardChart.initChart(_chart);
		category.add("", 0);
		
		TimeSeries series = new TimeSeries("Speed"){

			@Override
			public synchronized SortedMap<Double, Double> getRange(
					double start, double stop, int beforeAfterPoints) {
				SortedMap<Double, Double> map = super.getRange(start, stop, beforeAfterPoints);
				return Collections.unmodifiableSortedMap(map);
			}
			
		};
		dataset.addSeries(series);
		TimeChart chart = new TimeChart(dataset, chartRenderer);
	    chart.setDateFormat("");
//	    chartView = new GraphicalView(this, chart){
//
//			@Override
//			public boolean onTouchEvent(MotionEvent event) {
//				return false;
//			}
//	    	
//	    };
		chartView = (GraphicalView)this.findViewById(R.id.ftptest_chartview);
		chartView.initChart(chart);
	}
	
	protected void initDashboardRenderer(){
		dashboardRenderer.setInScroll(true);
		dashboardRenderer.setClickEnabled(false);
		dashboardRenderer.setShowLegend(false);
		dashboardRenderer.setShowLabels(false);
		SimpleSeriesRenderer r = new SimpleSeriesRenderer();
		r.setColor(Color.RED);
		dashboardRenderer.addSeriesRenderer(r);
		dashboardRenderer.setVisualTypes(new DialRenderer.Type[] { Type.ARROW });
		dashboardRenderer.setMinValue(MIN);
		dashboardRenderer.setAngleMin(285);
		dashboardRenderer.setMaxValue(MAX);
		dashboardRenderer.setAngleMax(75);
		dashboardRenderer.setPanEnabled(false);
		dashboardRenderer.setZoomEnabled(false);
	}
	
	protected void initChartRenderer(){		
		chartRenderer = new XYMultipleSeriesRenderer();
		chartRenderer.setBackgroundColor(Color.DKGRAY);
		chartRenderer.setApplyBackgroundColor(true);
		chartRenderer.setInScroll(true);
		chartRenderer.setClickEnabled(false);
		chartRenderer.setXTitle("时间");
		chartRenderer.setYTitle("速率(KB/s)");
		chartRenderer.setLabelsColor(Color.WHITE);
		
		XYSeriesRenderer r = new XYSeriesRenderer();
		r.setColor(Color.GREEN);
		r.setLineWidth(2);
		r.setPointStyle(PointStyle.POINT);
		chartRenderer.addSeriesRenderer(r);
		
		chartRenderer.setMargins(new int[]{20, 40, 10, 10});
		chartRenderer.setMarginsColor(Color.DKGRAY);
		chartRenderer.setShowGrid(true);
		chartRenderer.setXAxisMax(200);
		chartRenderer.setXAxisMin(0);
		
		chartRenderer.setShowAxes(true);
		chartRenderer.setYAxisMax(1024);
		chartRenderer.setYAxisMin(0);
		chartRenderer.setPanEnabled(true, false);
		chartRenderer.setZoomEnabled(false);
		chartRenderer.setShowLabels(true);
		chartRenderer.setShowLegend(false);
		chartRenderer.setGridColor(Color.DKGRAY);
		
//		chartRenderer.addYTextLabel(256, "256K");
//		chartRenderer.addYTextLabel(512, "512K");
//		chartRenderer.addYTextLabel(768, "768K");
//		chartRenderer.addYTextLabel(1024, "1M");
		
		chartRenderer.setYLabelsAlign(Align.RIGHT);
	}

	@Override
	public void onReceiveData(byte[] data, final HttpException exp, int tag) {
		if(pd != null && pd.isShowing()){
			pd.cancel();
		}
		if(exp != null) {
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					Toast.makeText(getApplicationContext(), exp.getMessage(), Toast.LENGTH_LONG).show();
				}
			}) ;
			return;
		}
		
		try {
			XmlXPathAdaptor xpathAdaptor = new XmlXPathAdaptor(new ByteArrayInputStream(data));
			
			host = ((Node)xpathAdaptor.evaluate("/NODE/FTP_IP", XPathConstants.NODE)).getTextContent().trim();
			port = ((Node)xpathAdaptor.evaluate("/NODE/FTP_PORT", XPathConstants.NODE)).getTextContent().trim();
			username = ((Node)xpathAdaptor.evaluate("/NODE/FTP_USER", XPathConstants.NODE)).getTextContent().trim();
			password = ((Node)xpathAdaptor.evaluate("/NODE/FTP_PASS", XPathConstants.NODE)).getTextContent().trim();
			dir = ((Node)xpathAdaptor.evaluate("/NODE/FTP_CATALOG", XPathConstants.NODE)).getTextContent().trim();
			fileName = ((Node)xpathAdaptor.evaluate("/NODE/FTP_FILE", XPathConstants.NODE)).getTextContent().trim();
		} catch (final XmlParserException e) {
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
				}
			}) ;
		} catch(final NullPointerException e) {
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					Toast.makeText(getApplicationContext(), "下发FTP配置信息不正确!", Toast.LENGTH_LONG).show();
				}
			});
		}

		return;
	}
	
	protected double dashboardValue(double realValue){
		double startV = 0;
		double startR = 0;
		double ratio = 1;
		double diffV = 0;
		double diffR = 0;
		if(realValue > 0 && realValue <= R256){
			startV = 0;
			startR = 0;
			diffR = 1536;
			diffV = R256;
		}
		if(realValue > R256 && realValue <= R512){
			startV = R256;
			startR = 1536;
			diffR = 3584 - startR;
			diffV = R256;
		}
		if(realValue > R512 && realValue <= R1024){
			startV = R512;
			startR = 3584;
			diffR = 5620 - startR;
			diffV = R512;
		}
		if(realValue > R1024 && realValue <= R3072){
			startV = R1024;
			startR = 5620;
			diffR = 7168 - startR;
			diffV = 2048.0;
		}
		if(realValue > R3072 && realValue <= R5120){
			startV = R3072;
			startR = 7168;
			diffR = 8704 - startR;
			diffV = 2048.0;
		}
		if(realValue > R5120 && realValue <= R10240){
			startV = R5120;
			startR = 8704;
			diffR = R10240 - startR;
			diffV = R5120;
		}
		ratio = (realValue - startV) / diffV;
		return startR + ratio * diffR;
	}
	
	protected void startFTPThread(){
		stopped = false;
				new Thread(new Runnable(){
					public void run(){
						pd.setTitle("FTP测试");
						pd.setMessage("FTP连接中...");
						uiRun(new Runnable(){
							public void run(){
								pd.show();
							}
						});
						
						do {
							testCount++;
							FTPClient client = new FTPClient();
							try {
								client.connect(host, Integer.parseInt(port));
								int code = client.getReplyCode();
								if (FTPReply.isPositiveCompletion(code)
										&& client.login(username, password)) {
									uiRun(new Runnable() {
										public void run() {
											if(pd.isShowing())pd.dismiss();
											tv_testCount.setText(testCount + "/" + testNumber);
										}
									});

									try {
										client.setFileType(FTP.BINARY_FILE_TYPE);
										client.enterLocalPassiveMode();
										doFTPOperation(client);
									} catch (Exception e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
								} else {
									client.disconnect();
								}
								if(stopped){
									client.abort();
								}
								client.logout();
								category.clear();
								category.add("", 0);
								dataset.getSeriesAt(0).clear();
								dataset.getSeriesAt(0).add(0, 0.1);
								updateChart();
								updateSummary();
								final int newCount = testCount;
								uiRun(new Runnable(){
									public void run(){
										appendTestResult(newCount);
									}
								});
							} catch (final Exception e) {
								uiRun(new Runnable() {
									public void run() {
										if (pd.isShowing()) {
											pd.dismiss();
										}
										Toast.makeText(
												FTPRateChartActivity.this,
												e.getMessage(),
												Toast.LENGTH_LONG).show();
									}
								});
							}
						} while(!stopped && testCount < testNumber);
						if(!stopped){
							uiRun(new Runnable(){
								public void run(){
									Toast.makeText(getBaseContext(), "测试完毕", Toast.LENGTH_LONG).show();
									stopTest();
								}
							});
						}
					}
				}).start();
			}
}
